package org.korecky.gnotedigitalorganizer.threads;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EventListener;
import java.util.EventObject;
import java.util.List;
import javax.swing.filechooser.FileSystemView;
import org.apache.commons.lang3.StringUtils;

public class DetectNewDeviceThread extends Thread {
    private boolean stop;
    private List<File> newDevices;

    /**
     * Constructor
     * 
     * @param Thread
     *            name
     * @param Class
     *            which will be call when a new device will be detected
     */
    public DetectNewDeviceThread(String threadName) {
	super(threadName);
    }

    public void run() {
	stop = false;
	List<File> originalFileRoots = ReadAllMountedDevices();
	while (!stop) {
	    List<File> currentFileRoots = ReadAllMountedDevices();
	    newDevices = new ArrayList<File>();
	    if (originalFileRoots.size() < currentFileRoots.size()) {
		// New device connected
		for (File currentFileRoot : currentFileRoots) {
		    boolean isNewDevice = true;
		    for (File originalFileRoot : originalFileRoots) {
			if (StringUtils.equals(currentFileRoot.getAbsolutePath(), originalFileRoot.getAbsolutePath())) {
			    isNewDevice = false;
			    break;
			}
		    }
		    if (isNewDevice)
			newDevices.add(currentFileRoot);
		}
	    }
	    else if (originalFileRoots.size() > currentFileRoots.size()) {
		// Device removed
		originalFileRoots = ReadAllMountedDevices();
	    }

	    if (newDevices.size() > 0) {
		originalFileRoots = ReadAllMountedDevices();
		fireNewDeviceDetectedEvent(new NewDeviceDetectedEvent(this));
	    }

	    try {
		sleep(1000);
	    }
	    catch (InterruptedException e) {
	    }
	}
    }

    private List<File> ReadAllMountedDevices() {
	// All systems
	FileSystemView fsv = FileSystemView.getFileSystemView();
	List<File> currentFileRoots = new ArrayList<File>(Arrays.asList(fsv.getRoots()));
	// Linux
	File mountsFile = new File("/proc/mounts");
	if (mountsFile.exists()) {
	    // read /proc/mounts
	    DataInputStream in = null;
	    try {
		FileInputStream fstream = new FileInputStream("/proc/mounts");
		in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		while ((strLine = br.readLine()) != null) {
		    String[] strLineColumns = strLine.split(" ");
		    if (strLineColumns.length > 1) {
			currentFileRoots.add(new File(strLineColumns[1].trim()));
		    }
		}
	    }
	    catch (Exception e) {// Catch exception if any
		e.printStackTrace();
	    }
	    finally {
		// Close the input stream
		if (in != null) {
		    try {
			in.close();
		    }
		    catch (IOException e) {
			e.printStackTrace();
		    }
		}
	    }
	}
	return currentFileRoots;
    }

    public void finish() {
	this.stop = true;
    }

    public List<File> getNewDevices() {
	return newDevices;
    }

    public void setNewDevices(List<File> newDevices) {
	this.newDevices = newDevices;
    }

    /**
     * Declare the event for detecting a new device
     * 
     * @author vkorecky
     * 
     */
    public class NewDeviceDetectedEvent extends EventObject {
	/** serialVersionUID */
	private static final long serialVersionUID = -6182908774057223463L;

	public NewDeviceDetectedEvent(Object source) {
	    super(source);
	}
    }

    /**
     * Declare the listener class for detecting a new device
     * 
     * @author vkorecky
     * 
     */
    public interface NewDeviceDetectedEventListener extends EventListener {
	public void newDeviceDetectedEventOccurred(NewDeviceDetectedEvent evt);
    }

    /**
     * Create the listener list
     */
    protected javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList();

    /**
     * This methods allows classes to register for NewDeviceDetectedEvents
     * 
     * @param listener
     */
    public void addNewDeviceDetectedEventListener(NewDeviceDetectedEventListener listener) {
	listenerList.add(NewDeviceDetectedEventListener.class, listener);
    }

    /**
     * This methods allows classes to unregister for NewDeviceDetectedEvents
     * 
     * @param listener
     */
    public void removeNewDeviceDetectedEventListener(NewDeviceDetectedEventListener listener) {
	listenerList.remove(NewDeviceDetectedEventListener.class, listener);
    }

    /**
     * This private class is used to fire NewDeviceDetectedEvents
     * 
     * @param evt
     */
    void fireNewDeviceDetectedEvent(NewDeviceDetectedEvent evt) {
	Object[] listeners = listenerList.getListenerList();
	// Each listener occupies two elements - the first is the listener class
	// and the second is the listener instance
	for (int i = 0; i < listeners.length; i += 2) {
	    if (listeners[i] == NewDeviceDetectedEventListener.class) {
		((NewDeviceDetectedEventListener) listeners[i + 1]).newDeviceDetectedEventOccurred(evt);
	    }
	}
    }
}